package com.yinxianren.main.study.netty.one;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.MessageToByteEncoder;

import java.util.Date;

public class TimeServerPOJO {

    private int port;

    public TimeServerPOJO(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();        // 用来接收进来的连接
        EventLoopGroup workerGroup = new NioEventLoopGroup();    // 用来处理已经被接收的连接
        System.out.println("准备运行端口：" + port);

        try {
            ServerBootstrap b = new ServerBootstrap();        // 启动NIO服务的辅助启动类
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)            // 这里告诉Channel如何接收新的连接
                    .childHandler( new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            System.out.println("######### protected void initChannel(SocketChannel ch) throws Exception###########");
                            // 自定义处理类
                            // 注意添加顺序
                            ch.pipeline().addLast(new TimeEncoderPOJO(),new TimeServerHandlerPOJO());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            // 绑定端口，开始接收进来的连接
            ChannelFuture f = b.bind(port).sync();

            // 等待服务器socket关闭
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        new TimeServerPOJO(port).run();

/*


        Charset utf8 = Charset.forName("UTF-8");
        ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);

        ByteBuf sliced = buf.slice(0, 14);  // 创建从0开始到14的新slice
        System.out.println(sliced.toString(utf8));  //打印 Netty in Action

        buf.setByte(0, (byte) '!');                 //更新索引为0的字节
        // 断言成功，说明slice之后两段数据共享
        boolean  how=buf.getByte(0) == sliced.getByte(0) ;
        System.out.println("how="+how);
       //这个说明 slice 返回的是原缓冲区的一个副本，共享同一片数据
        System.out.println("ByteBuf sliced="+sliced.toString(utf8));
        System.out.println("ByteBuf buf="+buf.toString(utf8));

*/


/*

        Charset utf8 = Charset.forName("UTF-8");
        ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);

        ByteBuf copy = buf.copy(0, 14);     // 注意这里使用了copy
        System.out.println(copy.toString(utf8));
//        buf.setByte(0, (byte) 'J');
//       // 断言成功，说明原数据修改对copy不影响
//        assert buf.getByte(0) != copy.getByte(0);
        buf.setByte(0, (byte) '!');                 //更新索引为0的字节
        // 断言成功，说明slice之后两段数据共享
        boolean  how=buf.getByte(0) == copy.getByte(0) ;
        System.out.println("how="+how);
        //这个说明 slice 返回的是原缓冲区的一个副本，共享同一片数据
        System.out.println("ByteBuf copy="+copy.toString(utf8));
        System.out.println("ByteBuf buf="+buf.toString(utf8));*/
    }

}

class Time {
    private final long value;

    public Time() {
        // 除以1000是为了使时间精确到秒
        this(System.currentTimeMillis() / 1000L);
    }

    public Time(long value) {
        this.value = value;
    }

    public long value() {
        return value;
    }

    @Override
    public String toString() {
        System.out.println("################Time.toString#############");
        return new Date((value()) * 1000L).toString();
    }
}

/**
 * 服务器数据编码类
 * @author Coder
 *
 */
class TimeEncoderPOJO extends MessageToByteEncoder<Time> {

    // 发送数据时调用
    // Netty 中使用 ByteBuf 代替 Java NIO 提供的 ByteBuffer 作为字节的容器
    @Override
    protected void encode(ChannelHandlerContext ctx, Time msg, ByteBuf out) throws Exception {
        System.out.println("################服务器数据编码类################");
        // 只传输当前时间，精确到秒
        out.writeInt((int)msg.value());
    }

}

/**
 * 服务器解码器
 * 连接建立时发送当前时间
 * @author Coder
 *
 *  Channel
 *
 * 　　  它代表一个用于连接到实体如硬件设备、文件、网络套接字或程序组件，能够执行一个或多个不同的 I/O 操作的开放连接。
 *      可以比作传入和传出数据的传输工具
 *
 *
 *  Callback（回调）
 *  它就是一个方法，提供给另一种方法作为引用，这样后者就可以在某个合适的时间调用前者
 *
 */
class TimeServerHandlerPOJO extends ChannelInboundHandlerAdapter {
    /**
     * 连接建立的时候并且准备进行通信时被调用
     */
    @Override
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
        // 发送当前时间信息
        ChannelFuture f = ctx.writeAndFlush(new Time());
        System.out.println("########TimeServerHandlerPOJO.channelActive########"+f.toString());
        // 发送完毕之后关闭 Channel
        f.addListener(ChannelFutureListener.CLOSE);
    }


    /**
     * 收到数据时调用
     *  当服务器收到消息时，会自动调用 ChannelRead 方法，因此我们可以重写该方法来实现对收到数据的处理当服务器收到消息时，
     *   会自动调用 ChannelRead 方法，因此我们可以重写该方法来实现对收到数据的处理
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // TODO 数据处理

        super.channelRead(ctx,msg);
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

